// GetAmped.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <shellapi.h>
#include <jni.h>

typedef jint (JNICALL *tJNI_CreateJavaVM)(JavaVM **pvm, void **penv, void *args);
extern BYTE g_encData[16592];
//////////////////////////////////////////////////////////////////////////
DWORD MT_items=625;
DWORD MT_mt[624];
DWORD MT_seedint;

void MT_seed(int a1)
{
	MT_mt[0] = a1;
	for(int i=1;i<624;++i)
		MT_mt[i]=69069 *MT_mt[i-1];
	MT_items=624;
}

DWORD MT_getnext()
{
	if(MT_items>=624)
	{
		if(MT_items==625)
			MT_seed(4357);
		DWORD i;
		for (i=0; i<227; ++i)
			MT_mt[i] = ((((MT_mt[i] ^ MT_mt[i+1])&0x7ffffffe)^MT_mt[i])>>1)^((0-(MT_mt[i+1]&1))&0x9908b0df)^MT_mt[i+397];
		for (; i<623; ++i)
			MT_mt[i] = ((((MT_mt[i] ^ MT_mt[i+1])&0x7ffffffe)^MT_mt[i])>>1)^((0-(MT_mt[i+1]&1))&0x9908b0df)^MT_mt[i-227];
		MT_mt[623] = ((((MT_mt[623] ^ MT_mt[0])&0x7ffffffe)^MT_mt[623])>>1)^((0-(MT_mt[0]&1))&0x9908b0df)^MT_mt[i-227];
		MT_items=0;
	}
	DWORD r = MT_mt[MT_items++];
	r ^= (r >> 11);
	r ^= ((r & 0xff3a58ad) << 7);
	r ^= ((r & 0xffffdf8c) << 15);
	r ^= (r >> 18);
	return (BYTE)r;
}

void MT_decrypt(BYTE *buf,DWORD size)
{
	MT_seed((MT_seedint++)+0x457F4481);

	while(size--)
		*buf++ ^= MT_getnext();
}
//////////////////////////////////////////////////////////////////////////
#define DEC_DEF(x) (MT_decrypt(decdata+encClassList[x].pos,encClassList[x].size),\
	env->DefineClass(encClassList[x].name,sysLoader,(jbyte*)(decdata+encClassList[x].pos),encClassList[x].size))

typedef struct _encClass
{
	const char* name;
	int pos;
	int size;
}encClass;
encClass encClassList[10]={
	"keel/bootstrap/KeelClassLoader",0x25bc,0x7a2,
	"keel/bootstrap/KarSource$CountableOutputStream",0x3fc,0x33d,
	"keel/bootstrap/KarSource$CryptKey",0xff8,0x5a8,
	"keel/bootstrap/KarSource$DecryptorInputStream",0x73c,0x464,
	"keel/bootstrap/KarSource$EncryptorOutputStream",0xba0,0x457,
	"keel/bootstrap/KarSource$KarEntry",0x144,0x2b6,
	"keel/bootstrap/KarSource$KarInfo",0x1ab8,0xb01,
	"keel/bootstrap/KarSource$KarInputStream",0x15a0,0x518,
	"keel/bootstrap/KarSource",0x2d60,0x136d,
	"keel/bootstrap/BootstrapException",0,0x141
};

jclass cKeelClassLoader,
cKarSource_CountableOutputStream,
cKarSource_CryptKey,
cKarSource_DecryptorInputStream,
cKarSource_EncryptorOutputStream,
cKarSource_KarEntry,
cKarSource_KarInfo,
cKarSource_KarInputStream,
cKarSource,
cBootstrapException;

// inline
// void saveClass(BYTE *d,int i)
// {
// 	char path[MAX_PATH];
// 	strcpy(path,encClassList[i].name);
// 	strcat(path,".class");
// 	FILE *fp=fopen(path,"wb");
// 	fwrite(d+encClassList[i].pos,1,encClassList[i].size,fp);
// 	fclose(fp);
// }

void loadBootStrap(JNIEnv *env,jobject sysLoader)
{
	BYTE *decdata=new BYTE[sizeof(g_encData)];
	memcpy(decdata,g_encData,sizeof(g_encData));
	
	cKeelClassLoader=DEC_DEF(0);
	cKarSource_CountableOutputStream=DEC_DEF(1);
	cKarSource_CryptKey=DEC_DEF(2);
	cKarSource_DecryptorInputStream=DEC_DEF(3);
	cKarSource_EncryptorOutputStream=DEC_DEF(4);
	cKarSource_KarEntry=DEC_DEF(5);
	cKarSource_KarInfo=DEC_DEF(6);
	cKarSource_KarInputStream=DEC_DEF(7);
	cKarSource=DEC_DEF(8);
	cBootstrapException=DEC_DEF(9);

	//for(int i=0;i<10;++i)
	//	saveClass(decdata,i);

	delete[] decdata;
}

int runJvm(tJNI_CreateJavaVM CreateJVM,int nParam,char **pParam)
{
	JavaVM *jvm=NULL;
	JNIEnv *env=NULL;
	JavaVMOption vm_opt[3]={0};
	vm_opt[0].optionString="-Xincgc";
	vm_opt[1].optionString="-Djava.library.path=.;libs";
	vm_opt[2].optionString="-Xmx256m";
	JavaVMInitArgs vm_args;
	vm_args.options=vm_opt;
	vm_args.version=JNI_VERSION_1_2;
	vm_args.nOptions=3;
	vm_args.ignoreUnrecognized=0;
	if(CreateJVM(&jvm,(void**)&env,&vm_args)<0)
	{
		MessageBox(NULL,"Couldn't create Java VM",NULL,MB_OK);
		return 1;
	}

	jclass classLoader=env->FindClass("java/lang/ClassLoader");
	if(classLoader==NULL)
	{
		MessageBox(NULL,"java/lang/ClassLoader not found.",NULL,MB_OK);
		return 1;
	}

	jmethodID getSysClassLoader=env->GetStaticMethodID(classLoader,"getSystemClassLoader","()Ljava/lang/ClassLoader;");
	if(getSysClassLoader==NULL)
	{
		MessageBox(NULL,"getSystemClassLoader not found.",NULL,MB_OK);
		return 1;	
	}

	jclass cla=env->FindClass("java/lang/Class");
	if(cla==NULL)
	{
		MessageBox(NULL,"Class not found.",NULL,MB_OK);
		return 1;
	}

	jmethodID forName=env->GetStaticMethodID(cla,"forName","(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
	if(forName==NULL)
	{
		MessageBox(NULL,"forName not found.",NULL,MB_OK);
		return 1;

	}

	loadBootStrap(env,env->CallStaticObjectMethod(classLoader,getSysClassLoader));
	
	if(cKeelClassLoader==NULL)
	{
		MessageBox(NULL,"Launcher not found.",NULL,MB_OK);
		return 1;
	}
	
	jmethodID Launchar_init=env->GetMethodID(cKeelClassLoader,"<init>", "(Ljava/lang/String;Ljava/lang/String;)V");
	if(Launchar_init==NULL)
	{
		MessageBox(NULL,"Launchar.<init> not found.",NULL,MB_OK);
		return 1;
	}
	
	jobject Launchar=env->NewObject(cKeelClassLoader,Launchar_init,env->NewStringUTF("keel.dat"),
		env->NewStringUTF("dhfuhsudfh98vhdsovnfdhiouer8u8hgjbkjciudsuifsjdiosajfn"));
	if(Launchar==NULL)
	{
		MessageBox(NULL,"Launchar couldn't constructed.",NULL,MB_OK);
		return 1;
	}

//  	jmethodID loadClass=env->GetMethodID(cKeelClassLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
//  	if(loadClass==NULL)
//  	{
//  		MessageBox(NULL,"loadClass not found.",NULL,MB_OK);
//  		return 1;
//  	}

	jclass cKeelRuntime=(jclass)env->CallStaticObjectMethod(cla,forName,env->NewStringUTF("keel.runtime.KeelRuntime"),TRUE,Launchar);
	if(cKeelRuntime==NULL)
	{
		MessageBox(NULL,"KeelRuntime not found.",NULL,MB_OK);
		return 1;
	}

	jmethodID KeelRuntime_main=env->GetStaticMethodID(cKeelRuntime,"main","([Ljava/lang/String;)V");
	if(KeelRuntime_main==NULL)
	{
		MessageBox(NULL,"KeelRuntime.main not found.",NULL,MB_OK);
		return 1;
	}

	jobjectArray paramArray=env->NewObjectArray((nParam?19:13),env->FindClass("java/lang/String"),0);
	env->SetObjectArrayElement(paramArray,0,env->NewStringUTF("-entrypath"));
	env->SetObjectArrayElement(paramArray,1,env->NewStringUTF("kar:program_getamped:amped.kar"));
	env->SetObjectArrayElement(paramArray,2,env->NewStringUTF("com.cyberstep.ring.client.Game"));
	env->SetObjectArrayElement(paramArray,3,env->NewStringUTF("-debug"));
	env->SetObjectArrayElement(paramArray,4,env->NewStringUTF("true"));
	//env->SetObjectArrayElement(paramArray,4,env->NewStringUTF("false"));
	env->SetObjectArrayElement(paramArray,5,env->NewStringUTF("-updatedebug"));
	env->SetObjectArrayElement(paramArray,6,env->NewStringUTF("false"));
	env->SetObjectArrayElement(paramArray,7,env->NewStringUTF("-useconfkar"));
	env->SetObjectArrayElement(paramArray,8,env->NewStringUTF("true"));
	env->SetObjectArrayElement(paramArray,9,env->NewStringUTF("-useresourcekar"));
	env->SetObjectArrayElement(paramArray,10,env->NewStringUTF("true"));
	env->SetObjectArrayElement(paramArray,11,env->NewStringUTF("-entusedict"));
	env->SetObjectArrayElement(paramArray,12,env->NewStringUTF("true"));

	if(nParam)
	{
		env->SetObjectArrayElement(paramArray,13,env->NewStringUTF("-user"));
		env->SetObjectArrayElement(paramArray,14,env->NewStringUTF(pParam[1]));
		env->SetObjectArrayElement(paramArray,15,env->NewStringUTF("-pass"));
		env->SetObjectArrayElement(paramArray,16,env->NewStringUTF(pParam[3]));
		env->SetObjectArrayElement(paramArray,17,env->NewStringUTF("-portal"));
		env->SetObjectArrayElement(paramArray,18,env->NewStringUTF(pParam[5]));
	}
	
	// -usedxgl true
	env->CallStaticVoidMethod(cKeelRuntime,KeelRuntime_main,paramArray);
	jvm->DestroyJavaVM();
	return 0;
}

int APIENTRY _tWinMain(HINSTANCE hInstance,
				   HINSTANCE hPrevInstance,
				   LPTSTR    lpCmdLine,
				   int       nCmdShow)
{

	char *cmd=new char[strlen(lpCmdLine)+1];
	strcpy(cmd,lpCmdLine);

	char *pParam[100];
	int nParam=0;
	for(char *p=strtok(cmd," ,\t\n");p;p=strtok(NULL," ,\t\n"))
	{
		pParam[nParam++]=p;
	}

	char path[MAX_PATH+1];
	GetCurrentDirectory(MAX_PATH,path);
	path[strlen(path)+1]=0;
	SHFILEOPSTRUCT fo={0};
	fo.wFunc=FO_MOVE;
	fo.pFrom="dists\\cache\\*";
	fo.pTo=path;
	fo.fFlags=FOF_NOCONFIRMMKDIR|FOF_NOCONFIRMATION|FOF_SILENT|FOF_MULTIDESTFILES; 
	SHFileOperation(&fo);

	HMODULE hJvm=LoadLibrary("JRE\\1.3.1\\bin\\hotspot\\jvm.dll");
	if(hJvm)
	{
		tJNI_CreateJavaVM CreateJVM=(tJNI_CreateJavaVM)GetProcAddress(hJvm,"JNI_CreateJavaVM");
		runJvm(CreateJVM,nParam,pParam);
		FreeLibrary(hJvm);
	}
	delete[] cmd;
	return 0;
}